package net.brokenroad.gamma.server;

import java.io.*;
import java.net.*;
import java.util.*;

import net.brokenroad.gamma.net.*;

public class Server extends Thread
{
	int port;
	ArrayList<ClientInfo> clients;
	private Socket socket;
	ObjectOutputStream out;
	ObjectInputStream in;
	private ServerOptions opts;

	public Server(Socket socket, int port)
	{
		super("ServerHelperThread");
		this.socket = socket;
		this.port = port;
		clients = new ArrayList<ClientInfo>(3);
		opts = new ServerOptions();
		opts.ensureIsSet("server_version_min", "000001");
		opts.ensureIsSet("server_version_max", "000001");
		opts.ensureIsSet("max_clients", new Integer(10));
	}

	public void run()
	{
		boolean running = true;
		try
		{
			out = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()));
			out.flush();
			in = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));

			Packet input = null;

			while (running)
			{
				try
				{
					input = (Packet) in.readObject();
					if (input != null)
						System.err.println("Received packet: " + input.toString(true));

					switch (input.getType())
					{
						case AUTH :
							switch(input.getDetails()[1])
							{
								/*
								 * Eventually these cases will encrypt the connection and
								 * proceed - right now, signal the client that encryption
								 * is unavailable
								 */
								case 0:
									handlePacketAUTH(input);
									break;
								case 1:
									out.writeObject(new Packet(Packet.PacketType.FAILURE, "AUTH", "yes"));
									break;
								case 2:
									out.writeObject(new Packet(Packet.PacketType.FAILURE, "AUTH", "no"));
									break;
								default:
									break;	
							}
							break;
						case CHANGE:
							handlePacketCHANGE(input);
							break;
						case CHAT:
							handlePacketCHAT(input);
							break;
						case DETAILS:
							handlePacketDETAILS(input);
							break;
						case FAILURE:
							handlePacketFAILURE(input);
							break;
						case JOIN:
							handlePacketJOIN(input);
							break;
						case LIST:
							handlePacketLIST(input);
							break;
						case QUIT:
							handlePacketQUIT(input);
							break;
						case REQUEST_INFO:
							handlePacketREQUEST(input);
							break;
						case SUCCESS:
							handlePacketSUCCESS(input);
							break;
						case VERSION:
							System.err.println("GOt a VERSION.");
							handlePacketVERSION(input);
							break;
						default :
							sendPacket(new Packet(Packet.PacketType.FAILURE));
					}
				} catch (StreamCorruptedException e)
				{
					System.err.println(".oO");
					System.err.println(e.getMessage());
					System.err.println(e.getCause().getMessage());
				}
			}
			out.close();
			in.close();
			socket.close();
		} catch (ClassNotFoundException cnfe)
		{
			System.err
			    .println("This copy of the server is fundamentally broken, please reinstall.");
		} catch (Exception e)
		{
		}
	}
	
	private void handlePacketVERSION(Packet p)
  {
		System.err.println("Entered handlePacket: " + p);
		int server_min = opts.getInt("server_version_min");
		int server_max = opts.getInt("server_version_max");
		System.err.println("Finished server version");
		int client_min = Integer.parseInt(p.getDetail1());
		int client_max = Integer.parseInt(p.getDetail2());
		System.err.println("Finished client version");
		
		if(client_min > server_max || server_min > client_max)
		{
			System.err.println("Version mismatch, sending error.");
			sendPacket(new Packet(Packet.PacketType.FAILURE, "VERSION", "no"));
			System.err.println("Sent a no-match packet.");
			return;
		}
		int version_to_use = (client_max > server_max) ? server_max : client_max;

		System.err.println("Found a good match: " + version_to_use);
		Packet ret = new Packet(Packet.PacketType.SUCCESS, "VERSION", new Integer(version_to_use).toString());
		sendPacket(ret);
		System.err.println("Sent good-match packet.");
  }

	private void handlePacketSUCCESS(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketREQUEST(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketQUIT(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketLIST(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketJOIN(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketFAILURE(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketDETAILS(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketCHAT(Packet p)
  {
	  // TODO Auto-generated method stub
	  
  }

	private void handlePacketAUTH(Packet p)
	{
		
	}
	
	private void handlePacketCHANGE(Packet p)
	{
		
	}
	
	private void sendPacket(Packet toSend)
	{
		System.err.println("Sending packet: " + toSend.toString(true));
		if(out == null)
		{
			System.err.println("Tried to send packet with closed stream: " + toSend.toString(true));
			return;
		}
		try
		{
			out.writeObject(toSend);
			out.flush();
		}
		catch(IOException e)
		{
			System.err.println("Hit an exception sending packet: " + toSend.toString(true));
			e.printStackTrace(System.err);
		}
		System.err.println("Packet was actually sent.");
	}
}
